﻿// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SnapObjects.Data
{
    internal static class ProcedureSchemaManager
    {
        private static int _cacheCount = 0;
        private readonly static ConcurrentQueue<DwProcedureSchema> _procedures = new ConcurrentQueue<DwProcedureSchema>();

        public static void SetSPCache(int cacheCount)
        {
            _cacheCount = cacheCount;

            if (_cacheCount == 0)
            {
                Clear();
            }
        }

        public static IReadOnlyList<IAdoDbParameter> GetProcedureParameter(DataContext dataContext, string sqlStatement)
        {
            //this function needs to optimized
            if (TryGetKey(dataContext, sqlStatement, false, new object[] { }, out var key))
            {
                var procedure = _procedures.FirstOrDefault(m => m.Key.Equals(key));

                if (procedure != null)
                {
                    return procedure.ProcedureSchema.Parameters;
                }
            }

            return CreateParameter(dataContext, sqlStatement);
        }

        private static IReadOnlyList<IAdoDbParameter> CreateParameter(DataContext context, string sqlText)
        {
            var procSchemas = context.AdoDbSchemaProvider.GetProcedureParameterSchema(sqlText);

            var procSchema = procSchemas.FirstOrDefault();

            if (procSchema != null)
            {
                return procSchema.Parameters;
            }

            return new List<IAdoDbParameter>();
        }

        private static bool TryGetKey(DataContext context, string sqlStatement, bool isRpc, object[] parameters, out string key)
        {
            key = String.Empty;

            if (!String.IsNullOrWhiteSpace(context.ContextOptions.ConnectionString))
            {
                key = $"{context.ContextOptions.ConnectionString}:{sqlStatement}";

                if (isRpc && parameters.Length > 0)
                {
                    var typeNames = new List<string>();

                    foreach (var p in parameters)
                    {
                        if (p != null)
                        {
                            typeNames.Add(p.GetType().Name);
                        }
                    }

                    if (typeNames.Count > 0)
                    {
                        key = $"{key}:{String.Concat(typeNames)}";
                    }
                }
            }

            return true;
        }

        private static void Clear()
        {
            _procedures.Clear();
        }


        private class DwProcedureSchema
        {
            public DwProcedureSchema(string key, ProcedureSchema procedureInfo)
            {
                this.Key = key;
                this.ProcedureSchema = procedureInfo;
            }

            public string Key { get; }
            public ProcedureSchema ProcedureSchema { get; }
        }
    }
}
